package com.hzw.core.ctx

import android.annotation.SuppressLint
import android.app.Activity
import android.app.Application
import android.app.Service
import android.app.backup.BackupAgent
import android.content.Context
import android.content.ContextWrapper
import android.os.Build
import androidx.annotation.RequiresApi

@SuppressLint("StaticFieldLeak")
private var internalCtx: Context? = null

val appCtx: Context
    get() = internalCtx ?: initAndGetAppCtxWithReflection()

fun Context.injectAsAppCtx() {
    require(!canLeakMemory()) {
        "这个Context($this)能造成内存泄露"
    }
    internalCtx = this
}

/**
 * 通过反射获取Application Context
 * 这个是在AppCtxProvider为初始化的时候才会调用
 * 只有当优先级比当前经常还高的时候，才会通过反射获取
 */
@SuppressLint("PrivateApi", "DiscouragedPrivateApi")
private fun initAndGetAppCtxWithReflection(): Context {
    // Fallback, should only run once per non default process.
    val activityThread = Class.forName("android.app.ActivityThread")
    val ctx = activityThread.getDeclaredMethod("currentApplication").invoke(null) as Context
    internalCtx = ctx
    return ctx
}

/**
 * 返回true，表示这个Context有可能会内存泄露
 */
fun Context.canLeakMemory(): Boolean = when (this) {
    is Application -> false
    is Activity, is Service, is BackupAgent -> true
    is ContextWrapper -> if (baseContext === this) true else baseContext.canLeakMemory()
    else -> applicationContext === null
}


/**
 * 创建可用受社保保护存储的Context
 * 当 SDK > Android N 时，通过懒加载创建， 否则直接创建
 *
 * 当设备已开机但用户尚未解锁设备时，Android 7.0 将在安全的“直接启动”模式下运行。为支持此模式，系统为数据提供了两个存储位置：
 * 凭据加密存储，这是默认存储位置，仅在用户解锁设备后可用。
 * 设备加密存储，该存储位置在“直接启动”模式下和用户解锁设备后均可使用。
 *
 * 默认情况下，应用不会在“直接启动”模式下运行。
 * 如果应用在“直接启动”模式下运行时需要访问数据，请使用设备加密存储。设备加密存储包含使用密钥加密的数据，该密钥只有在设备成功执行启动时验证后才可用。
 *
 * 对于应使用与用户凭据（如 PIN 码或密码）关联的密钥加密的数据，请使用凭据加密存储。凭据加密存储仅在用户成功解锁设备后可用，直到用户再次重启设备。如果用户在解锁设备后启用锁定屏幕，则不会锁定凭据加密存储。
 *
 * 详细介绍：https://developer.android.com/training/articles/direct-boot?hl=zh-cn
 */
inline val directBootCtx: Context get() = if (Build.VERSION.SDK_INT < 24) appCtx else deviceProtectedStorageCtx.value

@PublishedApi
@RequiresApi(24)
internal val deviceProtectedStorageCtx = lazy { appCtx.createDeviceProtectedStorageContext() }